Prisma Migrationの動作を確認してみた
はじめに
CX事業本部の佐藤智樹です。
今回はPrismaから最近(2020年12月)プレビュー版で提供開始されたMigration機能の挙動を確認してみます。少し動作やファイル構成を確認しただけなので、さらに使って分かったことがあれば追記や別記事でアップしていきます。
2021年2月3日時点の情報なので基本的には以下の公式チュートリアルやドキュメントを確認してください。実際動かした際のファイル構成がさらっと見たい場合などは参考になるかと思います。基本備忘録用の記事です。
環境情報
種別 | バージョン |
---|---|
Prisma | 2.15.0 |
yarn | 1.21.1 |
Node | 14.4.0 |
TypeScript | 4.1.3 |
Docker | 3.1.0 |
Postgres | 11(on Docker) |
サンプルコード
今回の記事で実行した結果のコードをリポジトリに置いてあります。全体的に構成が見たければこちらをご確認ください。
Prismaの導入
記事の最初で上げたチュートリアルに従いつつ、一部変更(npm->yarnなど)しながら試していきます。ほぼチュートリアルのままで、実際に生成されたファイルの紹介などがメインになります。
まずディレクトリを作成して、プロジェクトを立ち上げます。
$ mkdir hello-prisma-migration $ cd hello-prisma-migration $ yarn $ yarn add @prisma/cli typescript ts-node @types/node --save-dev
次にtsconfig.jsonファイルを生成しておきます。
{ "compilerOptions": { "sourceMap": true, "outDir": "dist", "strict": true, "lib": ["esnext"], "esModuleInterop": true } }
prisma init
コマンドでPrisma実行用のファイルが生成できます。
yarn prisma init yarn run v1.21.1 $ /Users/sato.tomoki/Documents/hogehoge/hello-prisma-migration/node_modules/.bin/prisma init Environment variables loaded from .env ✔ Your Prisma schema was created at prisma/schema.prisma. You can now open it in your favorite editor. warn Prisma would have added DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public" but it already exists in .env Next steps: 1. Set the DATABASE_URL in the .env file to point to your existing database. If your database has no tables yet, read https://pris.ly/d/getting-started 2. Set the provider of the datasource block in schema.prisma to match your database: postgresql, mysql or sqlite. 3. Run yarn prisma introspect to turn your database schema into a Prisma data model. 4. Run yarn prisma generate to install Prisma Client. You can then start querying your database. More information in our documentation: https://pris.ly/d/getting-started ✨ Done in 2.53s.
DBの接続設定
チュートリアルでもCLIの結果でも書かれてるように、まず接続する先のDATABASE_URL
を設定します。.env
ファイル内に書かれているものを編集します。今回は後ほど記載するPostgresの設定に合わせて記載します。
DATABASE_URL="postgresql://postgres:postgres@localhost:5433/test_db?schema=public"
初期modelの設定とマイグレーション
prisma init
時に生成されたprisma
ディレクトリ内のschema.prisma
ファイルを編集します。provider
は既にpostgresになっており、client
も変更不要なので今回使用するmodel
の追加だけ行います。
// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema datasource db { provider = "postgresql" url = env("DATABASE_URL") } generator client { provider = "prisma-client-js" } // add model model prisma_test { id Int @id @default(autoincrement()) timestamp DateTime created_at DateTime @default(now()) updated_at DateTime @default(now()) }
この状態で一度移行(初回実行時は新規作成)用のSQLを生成して実行します。
$ yarn prisma migrate dev --name init --preview-feature
実行すると以下のように移行用のmigration.sql
ファイルとmigration_lock.toml
ファイルが生成されます。
注意:この時点でDATABASE_URLの指定が誤っている場合エラーは出ませんが、DBに接続できないためmigration用のファイルだけ生成されてDB自体は更新されません。
-- CreateTable CREATE TABLE "prisma_test" ( "id" SERIAL NOT NULL, "timestamp" TIMESTAMP(3) NOT NULL, "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY ("id") );
# Please do not edit this file manually provider = "postgresql"
移行用のSQLファイルは出来ていますが更新履歴などがファイルでは確認できません。DBのスキーマを確認してみます。すると以下のように作成したテーブルと付随するシーケンス以外に、migration用の_prisma_migrations
テーブルが確認できます。
test_db=# \d List of relations Schema | Name | Type | Owner --------+--------------------+----------+---------- public | _prisma_migrations | table | postgres public | prisma_test | table | postgres public | prisma_test_id_seq | sequence | postgres (3 rows)
_prisma_migrations
の中身を見ると以下のようになっています。
test_db=# \x Expanded display is on. test_db=# select * from _prisma_migrations; -[ RECORD 1 ]-------+---------------------------------------------------------------- id | 302a2e38-652a-4b28-95a7-476f45ecc66e checksum | 806aa07f736c2f963fa81d6b3930faf415a23cd7ce3363cadd24acb1768a12c finished_at | 2021-02-02 14:44:42.525554+00 migration_name | 20210202144442_init logs | rolled_back_at | started_at | 2021-02-02 14:44:42.507204+00 applied_steps_count | 1
いつ更新されたのか、適応したmigrationの名前などが記録されています。
マイグレーション(2回目)
modelに項目を追加して再度マイグレーションしてみます。modelへ以下のようにhogehoge
カラムを追加します。
… // add model model prisma_test { id Int @id @default(autoincrement()) timestamp DateTime hogehoge String created_at DateTime @default(now()) updated_at DateTime @default(now()) }
再度マイグレーションを実行します。
$ yarn prisma migrate dev --name init --preview-feature
すると再度マイグレーション用のSQLファイルが生成されます。
/* Warnings: - Added the required column `hogehoge` to the `prisma_test` table without a default value. This is not possible if the table is not empty. */ -- AlterTable ALTER TABLE "prisma_test" ADD COLUMN "hogehoge" TEXT NOT NULL;
コメントにもあるようにこのSQLはもしテーブルにデータがあれば失敗します。今回はテーブルが空で成功しているようなのでDBを確認します。
test_db=# select * from _prisma_migrations; -[ RECORD 1 ]-------+---------------------------------------------------------------- id | 302a2e38-652a-4b28-95a7-476f45ecc66e checksum | 806aa07f736c2f963fa81d6b3930faf415a23cd7ce3363cadd24acb1768a12c finished_at | 2021-02-02 14:44:42.525554+00 migration_name | 20210202144442_init logs | rolled_back_at | started_at | 2021-02-02 14:44:42.507204+00 applied_steps_count | 1 -[ RECORD 2 ]-------+---------------------------------------------------------------- id | 91b0e424-5588-440d-8d1a-3fda77689006 checksum | 3bb3bb7f849e5c99ee94b231860bdcaddd4b6b121c5bf717cea8080cc6aab2a finished_at | 2021-02-03 02:11:54.061427+00 migration_name | 20210203021154_init logs | rolled_back_at | started_at | 2021-02-03 02:11:54.052891+00 applied_steps_count | 1 test_db=# \d prisma_test Table "public.prisma_test" Column | Type | Collation | Nullable | Default ------------+--------------------------------+-----------+----------+----------------------------------------- id | integer | | not null | nextval('prisma_test_id_seq'::regclass) timestamp | timestamp(3) without time zone | | not null | created_at | timestamp(3) without time zone | | not null | CURRENT_TIMESTAMP updated_at | timestamp(3) without time zone | | not null | CURRENT_TIMESTAMP hogehoge | text | | not null | Indexes: "prisma_test_pkey" PRIMARY KEY, btree (id)
先ほど実行したmigrationの結果が新たに追加されており、テーブル定義も変更されています。
失敗するマイグレーションの挙動確認
最後にテーブルにデータが入っている状態で、マイグレーションが失敗するパターンを確認してみます。以下のSQLでデータを1件追加します。
INSERT INTO prisma_test (timestamp, hogehoge) VALUES (now(), 'fugafuga') ; INSERT 0 1 test_db=# select * from prisma_test; -[ RECORD 1 ]----------------------- id | 1 timestamp | 2021-02-03 02:27:11.754 created_at | 2021-02-03 02:27:11.754 updated_at | 2021-02-03 02:27:11.754 hogehoge | fugafuga
この状態でmodelにカラム(必須属性)を追加します。
… // add model model prisma_test { id Int @id @default(autoincrement()) timestamp DateTime hogehoge String name String created_at DateTime @default(now()) updated_at DateTime @default(now()) }
yarn prisma migrate dev --name init --preview-feature yarn run v1.21.1 $ /Users/sato.tomoki/Documents/hogehoge/hello-prisma-migration/node_modules/.bin/prisma migrate dev --name init --preview-feature Environment variables loaded from .env Prisma schema loaded from prisma/schema.prisma Datasource "db": PostgreSQL database "test_db", schema "public" at "localhost:5433" Error: ⚠️ We found changes that cannot be executed: • Step 0 Added the required column `name` to the `prisma_test` table without a default value. There are 1 rows in this table, it is not possible to execute this migration. error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
既にデータがテーブルに存在するのでデフォルト値の無い必須属性追加はエラーとなりました。DB側でのマイグレーション実行記録もなく、テーブル定義の変更も入りませんでした。
実行自体が失敗したことでマイグレーションファイル自体が生成されず、ファイルの追加/変更もありませんでした。
NULL許容するように追加カラムの設定を変更したところマイグレーションが正常に動作しました。
… // add model model prisma_test { id Int @id @default(autoincrement()) timestamp DateTime hogehoge String name String? // 変更 created_at DateTime @default(now()) updated_at DateTime @default(now()) }
上記の仕組みからマイグレーションできないような定義変更の場合は、マイグレーションファイルが生成されず、実行結果も残らないことが分かります。
本番での実行
前章まではマイグレーションファイル生成とDBへの適応を同時に行うコマンドを使っていました。本番環境の場合は、開発環境で作成したマイグレーションファイルを使って以下のコマンドを実行することでDBへの適応だけを行うことができます。
$ yarn prisma migrate deploy --preview-feature
感想
まだ複雑な挙動を試したわけでは無いですが、簡単な用途であればプレビュー版でも使えるような感触でした。Prismaは便利なものだと思うので今後も使用してみたいと思います。
この記事を見て使って不具合などあった場合は、以下のissueでフィードバックを募集しているそうなので是非コメントしてください。